home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgramD2.iso
/
Borland
/
Borland C++ V5.02
/
CLASSSRC.PAK
/
THREAD.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-05-06
|
14KB
|
567 lines
//----------------------------------------------------------------------------
// Borland Class Library
// Copyright (c) 1993, 1997 by Borland International, All Rights Reserved
//
//$Revision: 5.15 $
//
// TThread class implementation
//----------------------------------------------------------------------------
#include <classlib/pch.h>
#include <classlib/thread.h>
#include <services/checks.h>
#include <classlib/pointer.h>
#include <process.h>
#include <stdlib.h>
DIAG_DEFINE_GROUP(Threads,1,0);
//----------------------------------------------------------------------------
// Semaphores Win32
//
#if defined(BI_PLAT_WIN32)
//----------------------------------------
// TMutex WIN32
void TMutex::Release()
{
::ReleaseMutex(*this);
}
//----------------------------------------
// TCountedSemaphore WIN32
void TCountedSemaphore::Release()
{
::ReleaseSemaphore(*this, 1, 0);
}
//----------------------------------------
// TEventSemaphore WIN32
void TEventSemaphore::Release()
{
// Events don't need to be released
}
//----------------------------------------
// TSemaphoreSet & its TLock for Win32
//
TSemaphoreSet::TSemaphoreSet(const TSemaphore* sems[], int size)
:
Sems(0)
{
int count = 0;
if (sems)
while (sems[count])
count++;
Count = count;
Size = size >= 0 ? size : count;
if (Size) {
Sems = CONST_CAST(const TSemaphore**, new TSemaphorePtr[Size]);
int i = 0;
if (sems)
for (; i < Count; i++)
Sems[i] = sems[i];
for (; i < Size; i++)
Sems[i] = 0;
}
}
//
TSemaphoreSet::~TSemaphoreSet()
{
delete[] Sems;
}
//
void TSemaphoreSet::Add(const TSemaphore& sem)
{
if (Count < Size)
Sems[Count++] = &sem;
}
//
void TSemaphoreSet::Remove(const TSemaphore& sem)
{
CHECK(Count <= Size);
for (int i = 0; i < Count; i++)
if (Sems[i] == &sem) {
for (int j = i; j < Count-1; j++) // Shift rest down to keep packed
Sems[j] = Sems[j+1];
Sems[Count-1] = 0;
return;
}
}
void TSemaphoreSet::Release(int index)
{
if (index >= 0)
CONST_CAST(TSemaphore*,Sems[index])->Release();
else
for (int i = 0; i < Count; i++)
CONST_CAST(TSemaphore*,Sems[i])->Release();
}
static HANDLE* newHandles(const TSemaphoreSet& set)
{
HANDLE* handles = new HANDLE[set.GetCount()];
for (int i = 0; i < set.GetCount(); i++) {
CHECK(set[i]);
handles[i] = *set[i]; // Assumes non-0 since i is in set range
}
return handles;
}
//
TSemaphoreSet::TLock::TLock(const TSemaphoreSet& set, TWaitWhat wait,
ulong timeOut, bool alertable)
:
Set(0)
{
TAPointer<THandle> handles = newHandles(set);
if (InitLock(set.Count, wait,
::WaitForMultipleObjectsEx(set.Count, handles, wait, timeOut, alertable)))
Set = &set;
}
//
TSemaphoreSet::TLock::TLock(ulong msgMask, const TSemaphoreSet& set,
TWaitWhat wait, ulong timeOut)
{
TAPointer<THandle> handles = newHandles(set);
if (InitLock(set.Count, wait,
::MsgWaitForMultipleObjects(set.Count, handles, wait, timeOut, msgMask)))
Set = &set;
}
//
// Init the Set and Locked members after a system wait call
//
bool TSemaphoreSet::TLock::InitLock(int count, TWaitWhat wait, int index)
{
if (index >= WAIT_OBJECT_0 && index < WAIT_OBJECT_0+count ||
index >= WAIT_ABANDONED_0 && index < WAIT_ABANDONED_0+count)
{
if (wait == WaitAny) {
if (index >= WAIT_ABANDONED_0)
index -= WAIT_ABANDONED_0;
Locked = index; // Just this one is locked
}
else
Locked = AllAquired; // They are all locked
return true;
}
else if (index == WAIT_OBJECT_0+count)
Locked = MsgWaiting;
else if (index == WAIT_TIMEOUT)
Locked = TimedOut;
else if (index == WAIT_IO_COMPLETION)
Locked = IoComplete;
return false;
}
//
TSemaphoreSet::TLock::~TLock()
{
Release();
}
//
void TSemaphoreSet::TLock::Release(bool relinquish)
{
if (Set) {
CONST_CAST(TSemaphoreSet*,Set)->Release(Locked);
if (relinquish)
Set = 0;
}
}
//----------------------------------------------------------------------------
// Semaphores OS/2
//
#elif defined(BI_PLAT_OS2)
#endif
//----------------------------------------------------------------------------
// TThread Win32 & OS/2
//
// TThread constructors
//
TThread::TThread()
:
#if defined(BI_PLAT_WIN32)
ThreadId(0),
#elif defined(BI_PLAT_OS2)
Priority(0),
#endif
Handle(0),
Stat(Created),
TerminationRequested(0),
Attached(false)
{
}
//
// Attach to a running thread
//
TThread::TThread(TCurrent)
:
#if defined(BI_PLAT_WIN32)
ThreadId(::GetCurrentThreadId()),
#elif defined(BI_PLAT_OS2)
Priority(0),
#endif
Handle(0),
Stat(Running),
TerminationRequested(0),
Attached(true)
{
::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(),
::GetCurrentProcess(), &Handle,
0, false, DUPLICATE_SAME_ACCESS);
}
//
//
//
TThread::TThread(const TThread&)
:
#if defined(BI_PLAT_WIN32)
ThreadId(0),
#elif defined(BI_PLAT_OS2)
Priority(0),
#endif
Handle(0),
Stat(Created),
TerminationRequested(0),
Attached(false)
{
}
//
// TThread assignment operator
//
// Used when assigning derived objects. Attempting to assign from a running
// object is an error, since the data fields in the running object can be
// changing asynchronously.
//
const TThread& TThread::operator =(const TThread& thread)
{
switch (GetStatus()) {
case Created:
case Suspended:
case Finished: {
if (this != &thread) {
Handle = 0;
#if defined(BI_PLAT_WIN32)
ThreadId = 0;
#elif defined(BI_PLAT_OS2)
Priority = 0;
#endif
Stat = Created;
TerminationRequested = false;
Attached = false;
}
return *this;
}
default:
throw TThreadError(TThreadError::AssignError);
}
#if defined(BI_COMP_MSC)
return *this; // Bogus return to make MSVC happy
#endif
}
//
// TThread destructor
//
// If the thread hasn't finished, destroying its control object is an error.
//
TThread::~TThread()
{
if (!Attached && (GetStatus() == Running || GetStatus() == Suspended))
throw TThreadError(TThreadError::DestroyBeforeExit);
#if defined(BI_PLAT_WIN32)
//
// The RTL calls CloseHandle in _endthread, so we shouldn't if the thread
// was started with _beginthreadNT(...).
# if !defined(BI_MULTI_THREAD_RTL)
::CloseHandle(Handle);
# endif
#elif defined(BI_PLAT_OS2)
#endif
}
//
// Starts the thread executing. The actual call depends on the operating system.
// After the system call we check status.
//
TThread::THandle TThread::Start()
{
// If Start() has already been called for this thread, release the
// previously created system thread object before launching a new one.
//
if ((GetStatus() != Created) && Handle) {
#if defined(BI_PLAT_WIN32)
::CloseHandle(Handle);
#elif defined(BI_PLAT_OS2)
#endif
}
#if defined(BI_PLAT_WIN32)
# if defined(BI_MULTI_THREAD_RTL)
Handle = (HANDLE)::_beginthreadNT(&TThread::Execute, 4096, this, 0, 0, &ThreadId);
# else
Handle = ::CreateThread(0, 0, &TThread::Execute, this, 0, &ThreadId);
# endif
#elif defined(BI_PLAT_OS2)
# if defined(BI_MULTI_THREAD_RTL)
Handle = ::_beginthread(&TThread::Execute, 4096, this);
# else
APIRET res =
::DosCreateThread(&Handle,
(void (__syscall*)(ulong))&TThread::Execute,
REINTERPRET_CAST(ulong,this),
FALSE,
4000);
# endif
#endif
if (Handle) {
TRACEX(Threads, 1, "Thread started [id:" << Handle << ']');
Stat = Running;
}
else {
TRACEX(Threads, 2, "Thread failed to start");
Stat = Invalid;
throw TThreadError(TThreadError::CreationFailure);
}
return Handle;
}
//
// It's an error to try to suspend a thread that hasn't been started or that
// has already terminated.
//
ulong TThread::Suspend()
{
switch (GetStatus()) {
case Created:
TRACEX(Threads, 2, "Illegal Created thread suspension [id:" << Handle << ']');
throw TThreadError(TThreadError::SuspendBeforeRun);
case Finished:
TRACEX(Threads, 2, "Illegal Finished thread suspension [id:" << Handle << ']');
throw TThreadError(TThreadError::SuspendAfterExit);
default:
#if defined(BI_PLAT_WIN32)
ulong res = ::SuspendThread(Handle);
if (res < MAXIMUM_SUSPEND_COUNT) // Else a problem
Stat = Suspended;
#elif defined(BI_PLAT_OS2)
ulong res = ::DosSuspendThread(Handle);
Stat = Suspended;
#endif
TRACEX(Threads, 0, "Thread suspended [id:" << Handle << ", Count:" << res << ']');
return res;
}
}
//
// It's an error to try to resume a thread that isn't suspended.
//
ulong TThread::Resume()
{
switch (GetStatus()) {
case Created:
TRACEX(Threads, 2, "Illegal Created thread resumption [id:" << Handle << ']');
throw TThreadError(TThreadError::ResumeBeforeRun);
case Running:
TRACEX(Threads, 2, "Illegal Running thread resumption [id:" << Handle << ']');
throw TThreadError(TThreadError::ResumeDuringRun);
case Finished:
TRACEX(Threads, 2, "Illegal Finished thread resumption [id:" << Handle << ']');
throw TThreadError(TThreadError::ResumeAfterExit);
default:
#if defined(BI_PLAT_WIN32)
ulong res = ::ResumeThread(Handle);
#elif defined(BI_PLAT_OS2)
ulong res = ::DosResumeThread(Handle);
#endif
TRACEX(Threads, 0, "Thread resumed [id:" << Handle << ", Count:" << res << ']');
if (res == 0)
Stat = Running;
return res;
}
}
//
// Mark the thread for termination.
//
void TThread::Terminate()
{
TRACEX(Threads, 1, "Thread termination requested [handle:" << Handle << ']');
TerminationRequested = true;
}
//
// Block until the thread terminates.
//
// IMPORTANT: the meaning of the 'timeout' parameter is different for NT and
// OS/2. Under NT it specifies how long to wait for termination. Under OS/2 it
// specifies whether to wait or to return immediately if the thread hasn't
// terminated.
//
ulong TThread::WaitForExit(ulong timeout)
{
TRACEX(Threads, 1, "Waiting for thread exit [id:" << Handle << ']');
if (Stat == Running)
#if defined(BI_PLAT_WIN32)
return ::WaitForSingleObject(Handle, timeout);
#elif defined(BI_PLAT_OS2)
return ::DosWaitThread(&Handle, timeout);
#endif
else
return (ulong)-1;
}
//
// See note for WaitForExit().
//
ulong TThread::TerminateAndWait(ulong timeout)
{
Terminate();
return WaitForExit(timeout);
}
//
// Set the thread's priority.
//
int TThread::SetPriority(int pri)
{
TRACEX(Threads, 1, "Thread priority changed to " << pri <<
" [id:" << Handle << ']');
#if defined(BI_PLAT_WIN32)
return ::SetThreadPriority(Handle, pri);
#elif defined(BI_PLAT_OS2)
APIRET res = ::DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, pri-Priority,
Handle);
if (res != 0)
Priority = pri;
return res;
#endif
}
//
//
//
int
TThread::Run()
{
TRACEX(Threads, 1, "Illegal Run() on base TThread [id:" << Handle << ']');
return -1;
}
//
// Run the thread. This static function is given as the thread start address,
// with 'this' thread object passed as the param. Invoke the Run() method on
// the thread
//
#if defined(BI_MULTI_THREAD_RTL)
void _USERENTRY TThread::Execute(void* thread)
{
STATIC_CAST(TThread*,thread)->Run();
STATIC_CAST(TThread*,thread)->Stat = Finished;
}
#elif defined(BI_PLAT_WIN32)
ulong _stdcall TThread::Execute(void* thread)
{
int code = STATIC_CAST(TThread*,thread)->Run();
STATIC_CAST(TThread*,thread)->Stat = Finished;
return code;
}
#elif defined(BI_PLAT_OS2)
void __stdcall TThread::Execute(ulong thread)
{
REINTERPRET_CAST(TThread*,thread)->Run();
REINTERPRET_CAST(TThread*,thread)->Stat = Finished;
}
#endif
//
// Alternative to returning from Run(). Called from within the thread that
// wants to exit early.
//
void
TThread::Exit(ulong code)
{
Stat = Finished;
#if defined(BI_PLAT_WIN32)
::ExitThread(code);
#elif defined(BI_PLAT_OS2)
#endif
}
//
// Call only when Stat claims that the thread is Running.
//
TThread::TStatus
TThread::CheckStatus() const
{
#if defined(BI_PLAT_WIN32)
uint32 code;
::GetExitCodeThread(Handle, &code);
if (code == STILL_ACTIVE)
#elif defined(BI_PLAT_OS2)
if (::DosWaitThread(CONST_CAST(THandle*,&Handle), DCWW_NOWAIT) == ERROR_THREAD_NOT_TERMINATED)
#endif
return Running;
else
return Finished;
}
//----------------------------------------------------------------------------
//
// TThread::TThreadError constructor
//
TThread::TThreadError::TThreadError(TErrorType type)
:
xmsg(MakeString(type)),
Type(type)
{
}
//
// TThread::TThreadError::MakeString()
//
// Translates an error code into a string.
//
string TThread::TThreadError::MakeString(TErrorType type)
{
static _TCHAR* Names[] = {
"Suspend() before Run()",
"Resume() before Run()",
"Resume() during Run()",
"Suspend() after Exit()",
"Resume() after Exit()",
"creation failure",
"destroyed before Exit()",
"illegal assignment",
"Multithreaded Runtime not selected",
};
string Msg;
Msg.reserve(40);
Msg = "Error[thread]: ";
Msg += Names[type];
return Msg;
}